package com.fdb.efp.basic.framework.excel;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.Sheet;

/**
 * Excel读取与写出的工具类
 * 
 * @author hengyh
 * @since 2019年3月15日 下午2:58:02
 * @version 1.0
 */
public class ExcelUtil {
	/**
	 * 读取某一sheet页的excel， 指定表头行数 <br>
	 * 
	 * @param filePath
	 *            文件路径
	 * @param object
	 *            模板
	 * @param sheetNo
	 *            sheet页编号
	 * @param headLine
	 *            表头行数
	 * @return
	 * @throws IOException
	 * @author hengyh
	 * @since 2019年3月15日 下午2:58:14
	 * @date 2019年3月15日 下午2:58:14
	 * @version 0.1
	 */
	public static <T extends BaseRowModel> List<T> readExcel(
			String filePath, 
			T object, 
			Integer sheetNo,
			Integer headLine) throws IOException {
		InputStream inputStream = null;
		try {
			inputStream = FileUtil.getFileInputStream(filePath);
			return readExcel(inputStream, object, sheetNo, headLine);
		} finally {
			inputStream.close();
		}
	}

	/**
	 * 读取xls，xlsx文件到object的列表中
	 * 
	 * @param filePath
	 *            文件路径，可以是resources下的路径，也可以是绝对路径
	 * @param object
	 *            模板
	 * @param headLine
	 *            表头行数
	 * @return 结果列表
	 * @throws IOException
	 * @author hengyh
	 * @since 2019年3月15日 下午2:58:42
	 * @date 2019年3月15日 下午2:58:42
	 * @version 0.1
	 */
	public static <T extends BaseRowModel> List<T> readExcel(
			String filePath, 
			T object, 
			Integer headLine)
			throws IOException {
		InputStream inputStream = null;
		try {
			inputStream = FileUtil.getFileInputStream(filePath);
			return readAll(inputStream, object, headLine);
		} finally {
			inputStream.close();
		}
	}

	/**
	 * 根据InputStream读取Excel
	 * 
	 * @param inputStream
	 *            输入流，inputStream的处理由调用者进行
	 * @param object
	 *            模板
	 * @param headLine
	 *            表头行数
	 * @return
	 * @author hengyh
	 * @since 2019年3月15日 下午2:59:28
	 * @date 2019年3月15日 下午2:59:28
	 * @version 0.1
	 */
	public static <T extends BaseRowModel> List<T> readAll(
			InputStream inputStream, 
			T object, 
			Integer headLine) {
		ObjectExcelListener<T> listener = new ObjectExcelListener<>();
		ExcelReader reader = new ExcelReader(inputStream, null, listener, false);
		List<Sheet> sheets = reader.getSheets();
		for (Sheet sheet : sheets) {
			sheet.setHeadLineMun(headLine);
			sheet.setClazz(object.getClass());
			reader.read(sheet);
		}
		return listener.getResult();
	}

	/**
	 * 读取某一sheet页的excel, 默认表头行数是1
	 * 
	 * @param inputStream
	 *            输入流，inputStream的处理由调用者进行
	 * @param object
	 *            模板
	 * @param sheetNo
	 *            sheet页编号
	 * @return
	 * @author hengyh
	 * @since 2019年3月15日 下午2:59:43
	 * @date 2019年3月15日 下午2:59:43
	 * @version 0.1
	 */
	public static <T extends BaseRowModel> List<T> readExcel(
			InputStream inputStream, 
			T object, 
			Integer sheetNo) {
		return readExcel(inputStream, object, sheetNo, 1);
	}

	/**
	 * 读取某一sheet页的excel， 指定表头行数
	 * 
	 * @param inputStream
	 *            输入流，inputStream的处理由调用者进行
	 * @param object
	 *            模板
	 * @param sheetNo
	 *            sheet页编号
	 * @param headLine
	 *            表头行数
	 * @return
	 * @author hengyh
	 * @since 2019年3月15日 下午2:59:58
	 * @date 2019年3月15日 下午2:59:58
	 * @version 0.1
	 */
	public static <T extends BaseRowModel> List<T> readExcel(
			InputStream inputStream, 
			T object, 
			Integer sheetNo,
			Integer headLine) {
		ObjectExcelListener<T> listener = new ObjectExcelListener<>();
		ExcelReader reader = new ExcelReader(inputStream, null, listener, false);
		reader.read(new Sheet(sheetNo, headLine, object.getClass()));
		return listener.getResult();
	}

	// 写excel

	/**
	 * 写excel 不需设置sheet页名称 使用默认表头行数 1 写入sheet页第一页 <br>
	 * 
	 * @param outputStream
	 * @param content
	 * @throws IOException
	 * @author hengyh
	 * @since 2019年3月15日 下午3:00:09
	 * @date 2019年3月15日 下午3:00:09
	 * @version 0.1
	 * 
	 * @see ExcelUtil#writeExcel(OutputStream, List, Integer, Integer, String)
	 */
	public static <T extends BaseRowModel> void writeExcel(
			OutputStream outputStream, 
			List<T> content)
			throws IOException {
		writeExcel(outputStream, content, 1);
	}

	/**
	 * 写excel，写入指定sheet页, 不需设置sheet页名称 使用默认表头行数 1 <br>
	 * 
	 * @param outputStream
	 * @param content
	 * @param sheetNo
	 * @throws IOException
	 * @author hengyh
	 * @since 2019年3月15日 下午3:00:47
	 * @date 2019年3月15日 下午3:00:47
	 * @version 0.1
	 * @see ExcelUtil#writeExcel(OutputStream, List, Integer, Integer, String)
	 */
	public static <T extends BaseRowModel> void writeExcel(
			OutputStream outputStream, 
			List<T> content, 
			Integer sheetNo)
			throws IOException {
		writeExcel(outputStream, content, sheetNo, 1);
	}

	/**
	 * 写excel，写入指定sheet页, 不需设置sheet页名称 <br>
	 * 
	 * @param outputStream
	 * @param content
	 * @param sheetNo
	 * @param headLine
	 * @throws IOException
	 * @author hengyh
	 * @since 2019年3月15日 下午3:01:13
	 * @date 2019年3月15日 下午3:01:13
	 * @version 0.1
	 * @see ExcelUtil#writeExcel(OutputStream, List, Integer, Integer, String)
	 */
	public static <T extends BaseRowModel> void writeExcel(
			OutputStream outputStream, 
			List<T> content, 
			Integer sheetNo,
			Integer headLine) throws IOException {
		writeExcel(outputStream, content, sheetNo, headLine, null, true);
	}

	/**
	 * 写excel，写入指定sheet页
	 * 
	 * @param outputStream
	 *            输出流
	 * @param content
	 *            内容
	 * @param sheetNo
	 *            sheet页
	 * @param headLine
	 *            表头行数
	 * @param sheetName
	 *            sheet页名称
	 * @param finish
	 *            是否close输出流
	 * @throws IOException
	 * @author hengyh
	 * @since 2019年3月15日 下午3:01:33
	 * @date 2019年3月15日 下午3:01:33
	 * @version 0.1
	 */
	public static <T extends BaseRowModel> void writeExcel(
			OutputStream outputStream, 
			List<T> content, 
			Integer sheetNo,
			Integer headLine, 
			String sheetName, 
			boolean finish) throws IOException {
		if (content == null || content.isEmpty()) {
			return;
		}

		ExcelWriter writer = null;
		try {
			Sheet sheet = new Sheet(sheetNo, headLine, content.get(0).getClass(), sheetName, null);
			writer = EasyExcelFactory.getWriter(outputStream);
			writer.write(content, sheet);
			// outputStream.flush();
		} finally {
			if (finish && writer != null) {
				writer.finish();
			}
		}
	}

	/**
	 * 写入多个sheet页
	 * 
	 * @param outputStream
	 *            输出流
	 * @param sheetNames
	 *            sheet页名称列表
	 * @param contents
	 *            内容
	 * @throws IOException
	 * @author hengyh
	 * @since 2019年3月15日 下午3:01:57
	 * @date 2019年3月15日 下午3:01:57
	 * @version 0.1
	 */
	public static <T extends BaseRowModel> void writeMultipleSheet(
			OutputStream outputStream, 
			List<String> sheetNames,
			List<List<T>> contents) throws IOException {
		if (contents == null || contents.isEmpty() || contents.get(0).isEmpty()) {
			return;
		}

		ExcelWriter writer = EasyExcelFactory.getWriter(outputStream);
		Class<? extends BaseRowModel> clazz = contents.get(0).get(0).getClass();
		for (int i = 0; i < contents.size(); i++) {
			List<T> content = contents.get(i);
			String sheetName = sheetNames.get(i);
			boolean finish = (i == contents.size() - 1);
			try {
				Sheet sheet = new Sheet(i + 1, 0, clazz, sheetName, null);
				writer.write(content, sheet);
			} finally {
				if (finish && writer != null) {
					writer.finish();
				}
			}
		}
	}

}